﻿using GCGame.Table;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Xml.Linq;
using UnityEditor;
using UnityEngine;
using UnityEngine.Events;


public enum ABState
{
    NoLoad,//无加载
    Load,//正在加载
    OverLoad//记载完成
}

public class ABData
{
    //Ab加载
    public AssetBundle ab;
    //记载次数
    public int count;
    //加载状态
    public ABState state;
}

public class ABLoadMgr : MonoSingleton<ABLoadMgr>
{
    /// <summary>
    /// AB包名路径
    /// </summary>
    /// AB包和数量
    public static Dictionary<string, ABData> ABdic = new Dictionary<string, ABData>();

    //  public List<>

    public static AssetBundleManifest manifest;

    /// <summary>
    /// /
    /// </summary>
    /// <typeparam name="T">加载类型</typeparam>
    /// <param name="Path">加载资源路径</param>
    /// <param name="action">回调路径</param>

    //AB包加载的基本路劲
    public string Path = Application.streamingAssetsPath + "/";
    /// <summary>
    /// 异步加载AB
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="path"> AB包的地址</param>

    /// <param name="resName"></param>
    /// <param name="action"></param>
    /// 
    Dictionary<int, Tab_ABResPath> abPath = new Dictionary<int, Tab_ABResPath>();
    public override void Awake()
    {
        base.Awake();
        GetPath();
    }

    /// <summary>
    /// 
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="id">通过ID</param>
    /// <param name="action">返回参数的方法</param>
    public void ASyncLoadAB<T>(int id, UnityAction<T> action) where T : UnityEngine.Object
    {
        StartCoroutine(ASyncLoad(id, action));

    }
    /// <summary>
    /// 同步加载
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="path">AB包的地址</param>
    /// <param name="action"></param>
    /// <param name="resName"></param>
    /// <returns></returns>
    public T LoadAB<T>(int id) where T : UnityEngine.Object
    {

        if (FindPath(id) == null)
        {
            Debug.LogError($"没有找到'{id}'包");
            return null;
        }
        AssetBundle ab = AssetBundle.LoadFromFile(Path + abPath[id].ABPath + "/" + abPath[id].ABName + ".u3d");
        //加载依赖
        GetDellManifest(ab, Path);
        if (ab == null)
        {
            Debug.LogError($"没有找到'{ab.name}'包");
        }
        else
        {
            if (!ABdic.ContainsKey(Path))
            {

                ABdic.Add(Path, new ABData() { ab = ab, count = 1, state = ABState.Load });
            }
            else
            {
                ABdic[Path].count += 1;
            }
        }

        T t = ABdic[Path].ab.LoadAsset<T>(abPath[id].ABResName);
        if (t == null)
        {
            Debug.LogError("没有找到AB包中的资源");
        }
        if (t is GameObject)
        {
            return Instantiate(t);
        }
        return t;



    }

    public Tab_ABResPath FindPath(int id)
    {
        if (abPath.ContainsKey(id))
        {
            return abPath[id];
        }
        return null;

    }


    public void GetPath()
    {
        Dictionary<int, List<Tab_ABResPath>> keyValues = TableManager.GetABResPath();
        foreach (var item in keyValues.Values)
        {
            for (global::System.Int32 i = 0; i < item.Count; i++)
            {
                if (!abPath.ContainsKey(item[i].Id))
                {
                    abPath.Add(item[i].Id, item[i]);
                }
            }

        }

    }



    /// <summary>
    /// 断掉AB继续加载
    /// </summary>
    /// <param name="Path"></param>
    private void AgainLoadAB(Tab_ABResPath data)
    {
        AssetBundle ab = AssetBundle.LoadFromFile(Path + data.ABPath + "/" + data.ABName + ".u3d");
        GetDellManifest(ab, this.Path);
        if (ab == null)
        {
            Debug.LogError($"没有找到'{ab.name}'包");
        }
        else
        {
            if (!ABdic.ContainsKey(Path))
            {

                ABdic.Add(Path, new ABData() { ab = ab, count = 1, state = ABState.Load });
            }
            else
            {
                ABdic[Path].count += 1;
            }
        }


    }
    /// <summary>
    /// 获取依赖加载AB包依赖项
    /// </summary>
    /// <param name="ab"></param>
    /// <param name="path"></param>

    private void GetDellManifest(AssetBundle ab, string path)
    {
        //获取依赖清单
        manifest = ab.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
        if (manifest == null)
        {
            return;
        }

        //获取依赖清单
        string[] manifests = manifest.GetAllDependencies(ab.name);

        //是否有依赖项

        for (int i = 0; i < manifests.Length; i++)
        {
            //判断AB字典中有没有，没有加载进行添加
            if (!ABdic.ContainsKey(manifests[i]))
            {
                AssetBundle maniAB = AssetBundle.LoadFromFile(manifests[i]);

                ABdic.Add(manifests[i], new ABData() { ab = maniAB, count = 1, state = ABState.Load });
            }
            //有的话进行数量家一用计数的方法来进行这个unload
            else if (ABdic.ContainsKey(manifests[i]))
            {
                //是否加载完成
                if (ABdic[manifests[i]].state == ABState.OverLoad)
                {
                    ABdic[manifests[i]].count += 1;
                }
                else
                {
                    Debug.Log("打断AB加载清楚已加载AB进行重新加载");
                    //卸载没加载好的
                    UnLoadAB(manifests[i], true);
                    //重新加载
                   
                    AgainLoadAB(FindABPath(manifests[i]));
                }

            }

        }


        //加载完毕改变状态
        ABdic[path].state = ABState.OverLoad;
    }

    private Tab_ABResPath FindABPath(string path)
    {
        foreach (var item in abPath)
        {
            if (item.Value.ABPath==path)
            {
                return item.Value;
                
            }
        }
        return null;
    }

    /// <summary>
    /// 卸载AB
    /// </summary>
    /// <param name="id"></param>
    /// <param name="isflag"></param>
   public void UnLoadAB(int id ,bool isflag)
    {
        Tab_ABResPath data= FindPath(id);
        // 卸载AB
        if (ABdic.ContainsKey(data.ABPath))
        {
            ABdic[data.ABPath].count -= 1;
            if (ABdic[data.ABPath].count == 0)
            {
                ABdic[data.ABPath].ab.Unload(isflag);
                ABdic.Remove(data.ABPath);
            }
        }

    }
    private void UnLoadAB(string path, bool isflag)
    {
        // 卸载AB
        if (ABdic.ContainsKey(path))
        {
            ABdic[path].count -= 1;
            if (ABdic[path].count == 0)
            {
                ABdic[path].ab.Unload(isflag);
                ABdic.Remove(path);
            }
        }

    }

    /// <summary>
    /// 异步加载资源
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="id"></param>
    /// <param name="action"></param>
    /// <returns></returns>
    /// <exception cref="ArgumentException"></exception>
    private IEnumerator ASyncLoad<T>(int id, UnityAction<T> action) where T : UnityEngine.Object
    {
        if (FindPath(id)==null)
        {
            action(null);
            
        }
        else
        {
            //判断AB包是否存在
            if (ABdic.ContainsKey(abPath[id].ABPath))
            {
                if (ABdic[abPath[id].ABPath].state != ABState.OverLoad)
                {
                    yield return ASyncLoad2(abPath[id]);
                }
            }
            // 异步加载资源
            AssetBundleCreateRequest assbulid = AssetBundle.LoadFromFileAsync(Path + abPath[id].ABPath + "/" + abPath[id].ABName + ".u3d");
            yield return assbulid;
            
            ABdic.Add(abPath[id].ABPath, new ABData() { ab = assbulid.assetBundle, count = 1, state = ABState.Load });
            
            AssetBundleRequest abrequest= ABdic[abPath[id].ABPath].ab.LoadAssetAsync<T>(abPath[id].ABResName);
            yield return abrequest;

            T res = abrequest.asset as T;
            // 错误处理：资源不存在
            if (res == null)
            {
                throw new ArgumentException($"无法从AssetBundle '{abPath[id].ABPath}' 中获取资源 '{abPath[id].ABResName}'。");
            }
            // 把获取的资源回调
            if (res is GameObject)
            {
                Debug.Log("This AssetBundle contains a GameObject.");
                action(Instantiate(res));

            }
            else
            {
                action(res);
            }

        }



    }

    /// <summary>
    /// 
    /// </summary>

    /// <param name="path"></param>
    /// <returns></returns>
    /// <exception cref="ArgumentException"></exception>
    private IEnumerator ASyncLoad2(Tab_ABResPath data)
    {
        //把加载的名字村栈和是否完成加载
        Stack<(string name, bool needAddDepends)> loadStack = new Stack<(string name, bool needAddDepends)>();
        loadStack.Push((data.ABPath, true));
        while (loadStack.Count > 0)
        {
            var (name, needAddDepends) = loadStack.Peek();

            // 跳过已完成的包
            if (ABdic[name].state == ABState.OverLoad)
            {
                ABdic[name].count += 1;
                loadStack.Pop();
                continue;
            }
            // 暂停等待正在加载的包
            if (ABdic[name].state == ABState.Load)
            {
                yield return null;
                continue;
            }
            // 先处理依赖包
            if (needAddDepends)
            {
                loadStack.Pop();
                loadStack.Push((name, false));

                foreach (var depend in manifest.GetAllDependencies(name))
                {
                    if (!ABdic.ContainsKey(data.ABPath))
                    {
                        loadStack.Push((depend, true));
                    }
                }

                continue;
            }

            // 异步加载AB包
            AssetBundleCreateRequest abCreateRequest = AssetBundle.LoadFromFileAsync(Path + "/");
            ABdic[name].ab = abCreateRequest.assetBundle;
            ABdic[name].state = ABState.Load;
            if (ABdic[name].ab == null)
            {
                throw new ArgumentException($"AssetBundle '{name}' 加载失败");
            }
            yield return abCreateRequest;
            // 加载完成
            ABdic[name].state = ABState.OverLoad;
        }
    }

}
